package org.honeynet.hbstats.statservlet;

import org.codehaus.jackson.JsonEncoding;
import org.codehaus.jackson.JsonFactory;
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.map.ObjectMapper;
import org.honeynet.hbstats.statservlet.cache.LatestValue;
import org.honeynet.hbstats.statservlet.cache.TSValue;
import org.honeynet.hbstats.statservlet.cache.ts_binary.TS_Binary_Cache;
import org.honeynet.hbstats.statservlet.cache.ts_binary.TS_Binary_LatestCache;
import org.honeynet.hbstats.statservlet.cache.ts_binary.TS_Binary_MD5Cache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.ws.rs.*;
import javax.ws.rs.core.StreamingOutput;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

@RequestScoped
@Path("/")
//@Stateless
public class BinChart {

    private final static Logger log = LoggerFactory.getLogger(BinChart.class);

    @Inject
    private TS_Binary_Cache tsCache;
    @Inject
    private TS_Binary_LatestCache latestCache;
    @Inject
    private TS_Binary_MD5Cache md5Cache;


    @GET
    @Path("/ts/bin/{md5:[0-9a-f]{32}}")
    @Produces("application/json")
    public StreamingOutput GET(
            @PathParam("md5") String md5,
            @QueryParam("b") long b,
            @QueryParam("e") long e
    ) {
        log.debug("request in");

        if (b > e || b < 0 || e < 0) {
            throw new WebApplicationException(400);
        }

        Long binary_id = md5Cache.get(md5);
        if (binary_id == null) {
            throw new WebApplicationException(404);
        }

        final List<TSValue> res = fetch(binary_id, b, e);
        if (res == null) {
            throw new WebApplicationException(418); //item is there but no data for it?
        }


        return new StreamingOutput() {
            @Override
            public void write(OutputStream outputStream) throws IOException, WebApplicationException {
                JsonFactory jsonFactory = new JsonFactory();
                JsonGenerator jg = jsonFactory.createJsonGenerator(outputStream, JsonEncoding.UTF8);
                jg.setCodec(new ObjectMapper());

                log.debug("res size: {}", res.size());
                log.debug("res: {}", res);
                jg.writeObject(res.toArray());

//                jg.writeStartArray();
//                for (TSValue tsv : res) {
//                    log.debug("writing out: {}", tsv);
//                    jg.writeObject(tsv);
//                    jg.flush();
//                }
//                jg.writeEndArray();

                jg.close();
            }
          };
    }


    private List<TSValue> fetch(long binary_id, long b, long e) {
        long curr_time = new java.util.Date().getTime();

        LatestValue lcv = latestCache.get(binary_id);
        if (lcv == null) {
            //item is there but no data for it?
            return null;
        }

        if (curr_time > lcv.time + 60 * 10000) {
            lcv = latestCache.refresh(binary_id);
        }

        long b_month;
        long e_month;
        long iterator_latest_month = Util.truncateToMonth(lcv.i_last_ts);
        long item_latest_month = Util.truncateToMonth(lcv.value);
        long fetch_immutables_from_month;
        Calendar cal;
        ArrayList<TSValue> res = new ArrayList<TSValue>();

        log.debug("item latest : {} month: {}", lcv.value, item_latest_month);

        if (e <= 0 || e > item_latest_month) {
            e_month = item_latest_month;
        }
        else {
            e_month = Util.truncateToMonth(e);
        }

        if (e_month > iterator_latest_month) { // month slipped beetween queries, but ignoring it
            fetch_immutables_from_month = iterator_latest_month;
            e_month = fetch_immutables_from_month;
        }
        else if (item_latest_month == iterator_latest_month) {
            // re-fetch for month
            TSValue tsv = tsCache.load(item_latest_month, binary_id);
            if (tsv == null) {
                //item is there but no data for it?
                return null;
            }
            res.add(tsv);

            cal = Util.getUTCCal(iterator_latest_month);
            cal.roll(Calendar.MONTH, -1);
            fetch_immutables_from_month = cal.getTimeInMillis();
        }
        else {
            fetch_immutables_from_month = e_month;
        }

        if (b <= 0) {
            b_month = fetch_immutables_from_month;
        }
        else {
            b_month = Util.truncateToMonth(b);
        }

        ArrayList<Long> immutables = new ArrayList<Long>();
        cal = Util.getUTCCal(fetch_immutables_from_month);
        long month_i = fetch_immutables_from_month;
        while (month_i > b_month) {
            immutables.add(month_i);
            cal.roll(Calendar.MONTH, -1);
            month_i = cal.getTimeInMillis();
        }
        immutables.add(b_month);

        for (long m : immutables) {
            TSValue tsv = tsCache.get(m, binary_id);
            if (tsv == null) {
                break;
            }
            res.add(tsv);
            log.debug("added new tsv: {}", tsv);
        }

        return res;
    }

}